Basic set of routines to handle errors and manage log file
!! Basic set of routines to handle errors and manage log file !|author: <a href="mailto:giovanni.ravazzani@polimi.it">Giovanni Ravazzani</a> ! license: <a href="http://www.gnu.org/licenses/">GPL ! !### History ! ! current version 1.2 - 3rd April 2010 ! ! | version | date | comment | ! |----------|-------------|----------| ! | 1.0 | 01/Feb/2007 | Original code | ! | 1.1 | 25/Sep/2008 | Code revised | ! | 1.2 | 03/Apr/2010 | separate log on file and screen | ! !### License ! license: GNU GPL <http://www.gnu.org/licenses/> ! ! This file is part of ! ! MOSAICO -- MOdular library for raSter bAsed hydrologIcal appliCatiOn. ! ! Copyright (C) 2011 Giovanni Ravazzani ! !### Module Description ! Basic set of routines to handle errors and manage log file. ! Three severity of log are defined: ! `error` (logLevel = 1): report only error messages ! `warning` (logLevel = 2): report error and warning messages ! `info` (logLevel = 3): report error, warning and informative messages ! The default logLevel is 3 (write all) and log messages are written ! on default output unit (screen).If necessary, the user can override ! default parameters by passing command line options. ! example of option passed from command line: ! ! ` executable -loglevel error ` ! ! The user can choose to write messages also on a file: ! ! ` executable -loglevel warning -logfile log.csv ` ! ! The module is initialized before the rest of the program takes place ! so it is better that this module is kept self containing and not to ! use utilities contained in other complicated modules that may want ! to use log facilities. MODULE LogLib ! Modules used: ! USE DataTypeSizes, ONLY : & ! Imported Type Definitions: short USE ErrorCodes, ONLY : & ! Imported Parameters: openFileError IMPLICIT NONE ! Global (i.e. public) Declarations: ! Global Scalars: LOGICAL :: verbose = .TRUE. !! display messages on the screen !! default value = TRUE INTEGER (KIND = short) :: logUnit !! file unit for log reporting ! Global Routines: PUBLIC :: LogInit PUBLIC :: LogStop PUBLIC :: LogMsg PUBLIC :: Catch ! Local Parameters: INTEGER (KIND = short), PRIVATE, PARAMETER :: stringLen = 500 INTEGER (KIND = short), PARAMETER :: stdIn = 5 !!standard input INTEGER (KIND = short), PARAMETER :: stdOut = 6 !!standar output ! Local Scalars: CHARACTER (LEN = stringLen), PRIVATE :: logFile !! file for log reporting CHARACTER (LEN = 1), PARAMETER, PRIVATE :: logsep ="," !!log separator LOGICAL :: logToScreen = .TRUE. LOGICAL :: logToFile = .FALSE. INTEGER (KIND = short), PRIVATE :: logLevel = 3 !!log level; default=3 INTEGER (KIND = short), PRIVATE :: ios ! Local Routines PRIVATE :: GetUnit PRIVATE :: TimeStamp !======= CONTAINS !======= ! Define procedures contained in this module. !============================================================================== !| Description: ! initialize global parameters SUBROUTINE LogInit IMPLICIT NONE ! Local scalars: INTEGER (KIND = short) :: i CHARACTER (LEN = stringLen) :: arg !! command line arguments CHARACTER (LEN = stringLen) :: auxString CHARACTER (LEN = stringLen) :: sep !------------end of declaration------------------------------------------------ i = 1 DO WHILE ( .not. (arg == '') ) CALL Getarg ( i, arg ) SELECT CASE (arg) CASE ( '-noverbose' ) verbose = .FALSE. logToScreen = .FALSE. CASE ( '-logfile' ) i = i + 1 CALL Getarg ( i , logFile ) logUnit = GetUnit() OPEN (UNIT = logUnit, FILE = logFile, position = 'APPEND', & ACTION = 'WRITE', IOSTAT = ios) IF ( ios /= 0 ) THEN !verbose = .TRUE. CALL Catch ('error', 'initialization', & 'error in opening log file: ' , & code = OpenFileError, argument = logFile ) ENDIF logToFile = .TRUE. CALL TimeStamp (logUnit) sep = " "//logsep//" "//"logging start" WRITE(UNIT = logUnit, FMT='(a)') TRIM(sep) CASE ( '-loglevel' ) i = i + 1 CALL Getarg ( i , arg ) SELECT CASE (arg) CASE ( 'error' ) !report only error messages logLevel = 1 CASE ( 'warning' ) !report error and warning messages logLevel = 2 CASE ( 'info' ) !report error, warning and info messages logLevel = 3 CASE default WRITE(stdOut,*) 'Unknown loglevel option: ', TRIM(arg) WRITE(stdOut,*) 'Program terminated' STOP '' END SELECT CASE DEFAULT !case unknown END SELECT i = i + 1 ENDDO RETURN END SUBROUTINE LogInit !============================================================================== !| Description: ! stop logging. If open, close the log file SUBROUTINE LogStop () IMPLICIT NONE ! Local scalars: CHARACTER (LEN = stringLen) :: sep LOGICAL :: fileIsOpen !------------end of declaration------------------------------------------------ IF (logToFile) THEN CALL TimeStamp (logUnit) sep = ' '//logsep//' '//'logging stop' WRITE(UNIT = logUnit, FMT='(a)') TRIM(sep) INQUIRE ( UNIT = logUnit, OPENED = fileIsOpen, IOSTAT = ios ) IF (fileIsOpen) THEN CLOSE (logUnit) END IF ENDIF END SUBROUTINE LogStop !============================================================================== !| Description: ! returns a free FORTRAN unit number ! Discussion: ! A "free" FORTRAN unit number is an integer between 1 and 99 which ! is not currently associated with an I/O device. A free FORTRAN unit ! number is needed in order to open a file with the OPEN command. ! If IUNIT = 0, then no free FORTRAN unit could be found, although ! all 99 units were checked (except for units 5 and 6). ! Otherwise, IUNIT is an integer between 1 and 99, representing a ! free FORTRAN unit. Note that GetUnit assumes that units 5 and 6 ! are special, and will never return those values. ! Adapted from John Burkardt FUNCTION GetUnit () & RESULT (iunit) IMPLICIT NONE ! Local scalars: INTEGER (KIND = short) :: iunit INTEGER (KIND = short) :: i LOGICAL :: lopen !------------end of declaration------------------------------------------------ iunit = 0 DO i = 1, 99 IF ( i /= 5 .AND. i /= 6 ) THEN INQUIRE ( unit = i, opened = lopen, iostat = ios ) IF ( ios == 0 ) THEN IF ( .NOT. lopen ) THEN iunit = i RETURN END IF END IF END IF END DO RETURN END FUNCTION GetUnit !============================================================================== !| Description: ! prints the current YMDHMS date as a time stamp. ! Example: `2008-09-29T21:00:25.624+0200` ! Adapted from John Burkardt SUBROUTINE TimeStamp & ! (unit) IMPLICIT NONE ! Subroutine arguments: ! Scalar arguments with intent (in): INTEGER (KIND = short), INTENT (IN) :: unit ! Local scalars: INTEGER (KIND = short) :: d CHARACTER ( LEN = 8 ) :: date INTEGER (KIND = short) :: h INTEGER (KIND = short) :: m INTEGER (KIND = short) :: mm INTEGER (KIND = short) :: n INTEGER (KIND = short) :: s CHARACTER ( LEN = 10 ) :: time INTEGER (KIND = short) :: values(8) INTEGER (KIND = short) :: y CHARACTER ( LEN = 5 ) :: zone !------------end of declaration------------------------------------------------ CALL date_and_time ( date, time, zone, values ) y = values(1) m = values(2) d = values(3) h = values(5) n = values(6) s = values(7) mm = values(8) WRITE (unit, '(i4,a1,i2.2,a1,i2.2,a1,i2,a1,i2.2,a1,i2.2,a1,i3.3,a5)', & ADVANCE = 'no' ) & y, '-', m, '-', d, 'T', h, ':', n, ':', s, '.', mm, zone RETURN END SUBROUTINE TimeStamp !============================================================================== !| Description: ! write a formatted string on specified unit- It is called by Catch routine SUBROUTINE LogMsg & ! (unit, level, process, comment, argument) IMPLICIT NONE ! Subroutine arguments ! Scalar arguments with intent(in): INTEGER (KIND = short), INTENT(in) :: unit CHARACTER (LEN = *), INTENT(in) :: level !! log level: info, warning, error CHARACTER (LEN = *), INTENT(in) :: process !! process which puts log CHARACTER (LEN = *), INTENT(in) :: comment !! comment on log CHARACTER (LEN = *), INTENT(in), OPTIONAL :: argument !! optional argument ! Local scalars: CHARACTER (LEN = 3) :: sep !------------end of declaration------------------------------------------------ CALL TimeStamp (unit) sep = ' '//logsep//' ' WRITE (unit,'(a)', ADVANCE = "no") sep WRITE (unit,'(a)', ADVANCE = "no") level WRITE (unit,'(a)', ADVANCE = "no") sep WRITE (unit,'(a)', ADVANCE = "no") process IF ( PRESENT (argument) ) THEN WRITE (unit,'(a)', ADVANCE = "no") sep WRITE (unit,'(a)', ADVANCE = "no") comment WRITE (unit,'(a)') TRIM(argument ) ELSE WRITE (unit,'(a)', ADVANCE = "no") sep WRITE (unit,'(a)') comment ENDIF END SUBROUTINE LogMsg !============================================================================== !| Description: ! exception handler SUBROUTINE Catch & ! (level, process, comment, code, argument) IMPLICIT NONE ! Subroutine arguments ! Scalar arguments with intent(in): CHARACTER (LEN = *), INTENT(in) :: level !! log level: info | warning | error CHARACTER (LEN = *), INTENT(in) :: process !! process which threw esception CHARACTER (LEN = *), INTENT(in) :: comment !! comment on exception INTEGER (KIND = short), OPTIONAL, INTENT(in) :: code !! error code to return CHARACTER (LEN = *), OPTIONAL, INTENT(in) :: argument !! optional argument ! Local Scalars: INTEGER (KIND = short) :: logStatus !------------end of declaration------------------------------------------------ !------------------------------------------------------------------------------ ![1.0] Define actual log level status !------------------------------------------------------------------------------ SELECT CASE ( level ) CASE ("error") logStatus = 1 CASE ("warning") logStatus = 2 CASE ("info") logStatus = 3 END SELECT !------------------------------------------------------------------------------ ![2.0] Put an entry on log file !------------------------------------------------------------------------------ IF (logToFile) THEN IF (logStatus <= logLevel) THEN IF ( PRESENT(argument) ) THEN CALL LogMsg(logUnit, level, process, comment, argument = argument) ELSE CALL LogMsg(logUnit, level, process, comment) ENDIF ENDIF ENDIF !------------------------------------------------------------------------------ ![3.0] Display on screen !------------------------------------------------------------------------------ IF (logToScreen) THEN IF (logStatus <= logLevel) THEN IF (verbose) THEN IF ( PRESENT(argument) ) THEN CALL LogMsg(stdOut, level, process, comment, argument = argument) ELSE CALL LogMsg(stdOut, level, process, comment) ENDIF ENDIF END IF END IF !------------------------------------------------------------------------------ ![4.0] Decide program termination !------------------------------------------------------------------------------ IF (level == 'error' ) THEN IF ( PRESENT (code) ) THEN IF (logToFile) THEN CALL LogStop () ENDIF !PAUSE CALL EXIT(code) ELSE IF (logToFile) THEN CALL LogStop () ENDIF !PAUSE CALL EXIT() ENDIF ENDIF END SUBROUTINE Catch END MODULE LogLib